#include "defines.h"
#include "kozos.h"
#include "intr.h"
#include "interrupt.h"
#include "serial.h"
#include "lib.h"
#include "consdrv.h"

#define SEND_BUFFER_SIZE 32

static int cons_used;

static unsigned char *send_buf; // リングバッファ
static int send_head;
static int send_len;

static kz_msgbox_id_t msgbox_in;
static kz_msgbox_id_t msgbox_out;

// 他プロセスからconsdrvにコマンド送信。commandはmallocで確保すること。
int send_to_cons(int size, char *command)
{
    if (msgbox_out == MSGBOX_NULL)
        return -1;

    if (!command || (command[0] != CONSDRV_CMD_USE &&
                     command[0] != CONSDRV_CMD_WRITE &&
                     command[0] != CONSDRV_CMD_CLOSE))
    {
        return -1;
    }

    return kz_send(msgbox_out, size, command, 1);
}

static void send_char()
{
    serial_send_byte(SERIAL_DEFAULT_DEVICE, send_buf[send_head]);

    if (++send_head == SEND_BUFFER_SIZE)
        send_head = 0;
    send_len--;
}

static void buffer_char(unsigned char c)
{
    if (send_len < SEND_BUFFER_SIZE - (c == '\n')) {
        if (send_len == 0) serial_intr_send_enable(SERIAL_DEFAULT_DEVICE);
        if (c == '\n') send_buf[(send_head + send_len++) % SEND_BUFFER_SIZE] = '\r';
        send_buf[(send_head + send_len++) % SEND_BUFFER_SIZE] = c;
    } else {
        puts("\nconsdrv: send buffer overflow !!\n");
    }
}

static void consdrv_intr()
{
    if (serial_is_error(SERIAL_DEFAULT_DEVICE)) {
        puts("\nserial error !!\n");
        serial_error_clear(SERIAL_DEFAULT_DEVICE); /* とりあえずクリアして回復を願う */
    }

    if (serial_is_recv_enable(SERIAL_DEFAULT_DEVICE)) {
        int c = serial_recv_byte(SERIAL_DEFAULT_DEVICE); // -1(エラー)ではないはず
        kz_send(msgbox_out, c, "<", 1);
    }

    if (serial_intr_is_send_enable(SERIAL_DEFAULT_DEVICE)
        && serial_is_send_enable(SERIAL_DEFAULT_DEVICE)) {
        kz_send(msgbox_out, 0, ">", 0);
        serial_intr_send_disable(SERIAL_DEFAULT_DEVICE);
    }
}

static int consdrv_command(int size, char *command)
{
    consdrv_cmd_header *hdr = (consdrv_cmd_header *)command;

    switch (hdr->cmd) {
        int ret;

        case CONSDRV_CMD_USE:
            ret = !cons_used;
            kz_send(hdr->mb, ret, "u", 0);
            if (ret) {
                msgbox_in = hdr->mb;
                cons_used = 1;
            }
            break;

        case CONSDRV_CMD_WRITE:
            buffer_char((unsigned char)hdr->car);
            break;

        case CONSDRV_CMD_CLOSE:
            ret = hdr->mb == msgbox_in;
            if (ret) { cons_used = 0; }
            kz_send(hdr->mb, ret, "c", 0);
            break;

        default:
            break;
    }

    return 0;
}

int consdrv_main(int argc, char *argv[])
{
    int c;
    char *p;

    cons_used = 0;

    send_buf = kz_kmalloc(SEND_BUFFER_SIZE);
    send_len = 0;
    send_head = 0;
    msgbox_out = kz_getmsgbox();

    kz_setintr(SOFTVEC_TYPE_SERINTR, consdrv_intr);
    serial_intr_recv_enable(SERIAL_DEFAULT_DEVICE);

    while (1) {
        kz_recv(msgbox_out, &c, &p);
        if (p[0] == '>') {
            send_char(); // send_lenが1減る!
            if (send_len) serial_intr_send_enable(SERIAL_DEFAULT_DEVICE);
        } else if (p[0] == '<') {
            if (cons_used) {
                kz_send(msgbox_in, c, "w", 0);
            } else { // エコーバック
                if (c == '\r') c = '\n';
                buffer_char((unsigned char)c);
            }
        } else {
            consdrv_command(c, p);
            kz_kmfree(p);
        }
    }

    /* ここには来ない(落ちることは考えてない)。 */

    return 0;
}
